package cs3343.project.family;

import javax.swing.JOptionPane;

/**
 * The Class GameController.
 */
public class GameController {
	
	/** The card controller. */
	private CardController cardController;
	
	/** The character controller. */
	private CharacterController characterController;
	
	/** The map controller. */
	private MapController mapController;
	
	/** The ui controller. */
	private UIController uiController;
	
	/** The statistics controller. */
	private StatisticsController statisticsController;
	
	/** The role controller. */
	private RoleController roleController;
	
	/** The dice. */
	private Dice dice;
	
	/** The clock. */
	private Clock clock;
	
	/** The current player index. */
	private int currentPlayerIndex;
	
	/** The round. */
	private int round;
	
	/** The running time. */
	private int runningTime;
	
	/** The max esteem. */
	private int maxEsteem;
	
	/** The is started. */
	private boolean isStarted = false;
	
	//for role fucntions
	/** The mum counter. */
	private int mumCounter = 0;
	
	/** The mum added. */
	private boolean mumAdded = false;
	
	/** The gma penalty skipped. */
	private boolean gmaPenaltySkipped = false;
	
	/** The ori se. */
	private int oriSE = -1;
	
	/** The new se. */
	private int newSE = -1;
	
	/**
	 * Instantiates a new game controller.
	 */
	public GameController() {
		characterController = new CharacterController(this);
		mapController = new MapController();
		statisticsController = new StatisticsController(this);
		uiController = new UIController(this);
		cardController = new CardController(this);
		roleController = new RoleController(this);
		dice = new Dice();
		clock = new Clock();

		currentPlayerIndex = 0;
		round = 1;
		runningTime = -1;
	}

	/**
	 * Gets the dice.
	 *
	 * @return the dice
	 */
	public Dice getDice() {
		return dice;
	}

	/**
	 * Sets the dice.
	 *
	 * @param dice the new dice
	 */
	public void setDice(Dice dice) {
		this.dice = dice;
	} 

	/**
	 * Gets the current player index.
	 *
	 * @return the current player index
	 */
	public int getCurrentPlayerIndex() {
		return currentPlayerIndex;
	}

	/**
	 * Sets the current player index.
	 *
	 * @param currentPlayerIndex the new current player index
	 */
	public void setCurrentPlayerIndex(int currentPlayerIndex) {
		this.currentPlayerIndex = currentPlayerIndex;
	}

	/**
	 * Gets the round.
	 *
	 * @return the round
	 */
	public int getRound() {
		return round;
	}

	/**
	 * Sets the round.
	 *
	 * @param round the new round
	 */
	public void setRound(int round) {
		this.round = round;
	}
	
	/**
	 * Game won.
	 *
	 * @return true, if successful
	 */
	public boolean gameWon() {
		for (int i = 0, esteemCounter = 0; i < characterController.getNumOfPlayer(); i++) {
			if (characterController.getPlayer(i).getEsteem() >= ConfigManager.getWin_esteem())
				esteemCounter++;
			if (esteemCounter == characterController.getNumOfPlayer()) {
				statisticsController.addGameLog("勝出遊戲");
				maxEsteem = findMaxEsteem();
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Game lost.
	 *
	 * @param currentPlayer the current player
	 * @return true, if successful
	 */
	public boolean gameLost(int currentPlayer) {

		if (characterController.getPlayer(currentPlayer).getEsteem() < ConfigManager.getLost_esteem()) {
			if (characterController.getPlayer(currentPlayer).getDiedOnce()) {
				statisticsController.addGameLog("遊戲落敗");
				return true;
			}
			else if (isStarted) {
				//one chance 
				JOptionPane.showMessageDialog(null, "You have one chance to revive, self-esteem point +100.");
				characterController.getPlayer(currentPlayer).setDiedOnce(true);
				updateEsteem(characterController.getPlayer(currentPlayer), 100, "自尊值過低，重生一次");
			}
		}
		return false;
	}
	
	/**
	 * Find max esteem.
	 *
	 * @return the int
	 */
	public int findMaxEsteem() {
		int max = 0;
		for (int i = 0; i < characterController.getNumOfPlayer(); i++)
			if (characterController.getPlayer(i).getEsteem() > max)
				max = characterController.getPlayer(i).getEsteem();
		return max;
	}
	
	/**
	 * Roll dice.
	 */
	public void rollDice() {

		int ran = dice.rollDice(1, 6);	
		
		//allow player to press start button, setup lower panel 
		if (!isStarted) {
			uiController.getGameFrame().initStatusBar();
			
			for(int i = 0; i < characterController.getNumOfPlayer(); i++)
				uiController.getGameFrame().firstSetPlayerIconLocation(i);

			roleController.setRoleFunctionButtonText();
			isStarted = true;
		}
		
		else if (isStarted) {
			dice.showDiceImage(ran);

			uiController.getGameFrame().setPlayerLand(ran, characterController.getCurrentPlayer());

			//[special card] exchange character card 
			if (Math.random() < 0.05) {
				Card specialCard = cardController.getMagicToolCard(String.valueOf(12));
				JOptionPane.showMessageDialog(null, 
						String.format(ConfigManager.getLangString("msgGetCard"), specialCard.getName(), specialCard.getDescription()) 
						, "特殊", 1);
				characterController.getCurrentPlayer().addCard(specialCard);
				statisticsController.addplayerLog(characterController.getCurrentPlayer(), "得到身份轉移卡");
			}
			else {
				//for GMA function
				oriSE = characterController.getCurrentPlayer().getEsteem();
				
				playerDrawCard();
			}
		}
	}
	
	//[called by card frame] move to next player until the pop up frame is closed
	/**
	 * After pressed.
	 */
	public void afterPressed() {		
		//for GMA function
		newSE = characterController.getCurrentPlayer().getEsteem();
		
		//check if role functions are applicable
		roleController.passiveRoleFunction();
				
		//save each player's esteem point of this turn
		for (int i = 0; i < characterController.getNumOfPlayer(); i++)
			statisticsController.addEsteemChange(i, characterController.getPlayer(i).getEsteem());
		
		//Check lost
		if (gameLost(currentPlayerIndex)) {
			uiController.hideGameFrame();
			uiController.showEndGameFrame();		
		}
		
		//Check end game	
		if (gameWon()) {
			uiController.hideGameFrame();
			uiController.showEndGameFrame();
		}
		
		//update round num
		if (currentPlayerIndex + 1 == characterController.getNumOfPlayer()) {
			uiController.getGameFrame().setRoundLabel(++round);

			//for MUM, GMA function
			gmaPenaltySkipped = false;
			mumAdded = false;
		}
		
		//move to next player
		//for BRO function
		if (!roleController.broRoleFunction())
			setCurrentPlayerIndex((currentPlayerIndex + 1) % characterController.getNumOfPlayer());
		
		uiController.getGameFrame().setPlayerPanel();
		
		roleController.setRoleFunctionButtonText();
	}
		
	/**
	 * Player draw card.
	 */
	public void playerDrawCard() {
		int ranCardType = (int) (4 * Math.random() + 1);
		Card ranCard = cardController.drawCard(ranCardType);

		if (ranCardType == 1) {
			uiController.showEventCardFrame(ranCard, characterController.getCurrentPlayer());
		}
		else if (ranCardType == 2) {
			uiController.showToolCardFrame(ranCard, characterController.getCurrentPlayer(), "ToolCard" );
		}
		else if(ranCardType == 3) {
			uiController.showYesNoFrame(ranCard, characterController.getCurrentPlayer(), "ActionCard");
		}
		else if(ranCardType == 4) {
			uiController.showYesNoFrame(ranCard, characterController.getCurrentPlayer(), "OralCard");
		}
		
		statisticsController.addplayerLog(characterController.getCurrentPlayer(), "抽到  " + ranCard.getName());
		
		//update center self-esteem point 
		//eventcard's long delay need to do esteem update inside EventCardFrame 
		if (ranCardType != 1)
			uiController.getGameFrame().setPlayerEsteemLabel(currentPlayerIndex);
	}
	
	/**
	 * Select character.
	 *
	 * @param title the title
	 * @param msg the msg
	 * @return the character
	 */
	public Character selectCharacter(String title, String msg) {
		String initName = "";

	    Object[] playerSelection = new Object[characterController.getNumOfPlayer() - 1];
	    for (int i = 0, j = 0; i < characterController.getNumOfPlayer(); i++) {
	    	if (!characterController.getPlayer(i).getName().equalsIgnoreCase(characterController.getCurrentPlayer().getName())) {
	    		playerSelection[j] = characterController.getPlayer(i).getName();
	    		if (j == 0)
	    			initName = characterController.getPlayer(i).getName();
	    		j++;
	    	}
	    }
	    
	    String initialSelection = initName;
	    Object selection = JOptionPane.showInputDialog(null, msg,
	        title, JOptionPane.QUESTION_MESSAGE, null, playerSelection, initialSelection);

	    for (int i = 0; i < characterController.getNumOfPlayer(); i++)
	    	if (characterController.getPlayer(i).getName().equalsIgnoreCase(String.valueOf(selection)))
	    		return characterController.getPlayer(i);
	    
	    return null;	    		
	}
	
	
	
	/**
	 * Update esteem.
	 *
	 * @param player the player
	 * @param newEsteem the new esteem
	 */
	public void updateEsteem(Character player, int newEsteem) {
		player.setEsteem(newEsteem);
		//statisticsController.addEsteemChange(characterController.getPlayerOrder(player), newEsteem);
	}
	
	/**
	 * Update esteem.
	 *
	 * @param playerIndex the player index
	 * @param newEsteem the new esteem
	 */
	public void updateEsteem(int playerIndex, int newEsteem) {
		characterController.getPlayer(playerIndex).setEsteem(newEsteem);
		//statisticsController.addEsteemChange(playerIndex, newEsteem);
	}
	
	/**
	 * Update esteem.
	 *
	 * @param player the player
	 * @param newEsteem the new esteem
	 * @param time the time
	 * @param event the event
	 */
	public void updateEsteem(Character player, int newEsteem, int time, String event) {
		player.setEsteem(newEsteem);
		statisticsController.addplayerLog(characterController.getPlayerOrder(player), time, event);
	}
	
	/**
	 * Update esteem.
	 *
	 * @param player the player
	 * @param newEsteem the new esteem
	 * @param event the event
	 */
	public void updateEsteem(Character player, int newEsteem, String event) {
		player.setEsteem(newEsteem);
		statisticsController.addplayerLog(characterController.getPlayerOrder(player), event);		
	}
	
	/**
	 * Update esteem.
	 *
	 * @param playerIndex the player index
	 * @param newEsteem the new esteem
	 * @param time the time
	 * @param event the event
	 */
	public void updateEsteem(int playerIndex, int newEsteem, int time, String event) {
		characterController.getPlayer(playerIndex).setEsteem(newEsteem);
		statisticsController.addplayerLog(playerIndex, time, event);
	}
	
	/**
	 * Exchange character.
	 *
	 * @param toExchange the to exchange
	 * @param beExchanged the be exchanged
	 */
	public void exchangeCharacter(Character toExchange, Character beExchanged) {
		Role tempRole = toExchange.getRole(); 
		int tempMapAreaId = toExchange.getMapAreaId();
		int tempEsteem = toExchange.getEsteem();
		boolean tempDiedOnce = toExchange.getDiedOnce();
		
		toExchange.setRole(beExchanged.getRole());
		toExchange.setMapAreaId(beExchanged.getMapAreaId());
		toExchange.setEsteem(beExchanged.getEsteem());
		toExchange.setDiedOnce(beExchanged.getDiedOnce());
		
		beExchanged.setRole(tempRole);
		beExchanged.setMapAreaId(tempMapAreaId);
		beExchanged.setEsteem(tempEsteem);
		beExchanged.setDiedOnce(tempDiedOnce);
		
		String msg = String.format(ConfigManager.getLangString("msgRoleExchange"),
				toExchange.getName(),
				toExchange.getRole().getRoleName(),
				String.valueOf(toExchange.getMapAreaId()),
				String.valueOf(toExchange.getEsteem()),
				toExchange.getDiedOnce() ? ConfigManager.getLangString("lblYes") : ConfigManager.getLangString("lblNo"),
				beExchanged.getName(),
				beExchanged.getRole().getRoleName(),
				String.valueOf(beExchanged.getMapAreaId()),
				String.valueOf(beExchanged.getEsteem()),
				beExchanged.getDiedOnce() ? "Yes" : "No");
		
		JOptionPane.showMessageDialog(null,	msg);
		uiController.getGameFrame().setPlayerPanel();
		uiController.getGameFrame().setPlayerLand(toExchange.getMapAreaId(), toExchange);
		uiController.getGameFrame().setPlayerLand(beExchanged.getMapAreaId(), beExchanged);
		afterPressed();
	}	
	
	/**
	 * Gets the card controller.
	 *
	 * @return the card controller
	 */
	public CardController getCardController() {
		return cardController;
	}

	/**
	 * Sets the card controller.
	 *
	 * @param cardController the new card controller
	 */
	public void setCardController(CardController cardController) {
		this.cardController = cardController;
	}

	/**
	 * Gets the character controller.
	 *
	 * @return the character controller
	 */
	public CharacterController getCharacterController() {
		return characterController;
	}

	/**
	 * Sets the character controller.
	 *
	 * @param characterController the new character controller
	 */
	public void setCharacterController(CharacterController characterController) {
		this.characterController = characterController;
	}

	/**
	 * Gets the map controller.
	 *
	 * @return the map controller
	 */
	public MapController getMapController() {
		return mapController;
	}

	/**
	 * Sets the map controller.
	 *
	 * @param mapController the new map controller
	 */
	public void setMapController(MapController mapController) {
		this.mapController = mapController;
	}

	/**
	 * Gets the ui controller.
	 *
	 * @return the ui controller
	 */
	public UIController getUiController() {
		return uiController;
	}

	/**
	 * Sets the ui controller.
	 *
	 * @param uiController the new ui controller
	 */
	public void setUiController(UIController uiController) {
		this.uiController = uiController;
	}
	
	/**
	 * Gets the role controller.
	 *
	 * @return the role controller
	 */
	public RoleController getRoleController() {
		return roleController;
	}

	/**
	 * Gets the clock.
	 *
	 * @return the clock
	 */
	public Clock getClock() {
		return clock;
	}

	/**
	 * Sets the clock.
	 *
	 * @param clock the new clock
	 */
	public void setClock(Clock clock) {
		this.clock = clock;
	}

	/**
	 * Sets the checks if is started.
	 *
	 * @param b the new checks if is started
	 */
	public void setIsStarted(boolean b){
		this.isStarted = b;
	}
	
	/**
	 * Gets the checks if is started.
	 *
	 * @return the checks if is started
	 */
	public boolean getIsStarted(){
		return this.isStarted;
	}
	
	/**
	 * Gets the running time.
	 *
	 * @return the running time
	 */
	public int getRunningTime() {
		return runningTime;
	}

	/**
	 * Sets the running time.
	 *
	 * @param runningTime the new running time
	 */
	public void setRunningTime(int runningTime) {
		this.runningTime = runningTime;
	}
	
	/**
	 * Gets the new se.
	 *
	 * @return the new se
	 */
	public int getNewSE() {
		return newSE;
	}
	
	/**
	 * Gets the ori se.
	 *
	 * @return the ori se
	 */
	public int getOriSE() {
		return oriSE;
	}
	
	/**
	 * Gets the mum counter.
	 *
	 * @return the mum counter
	 */
	public int getMumCounter() {
		return mumCounter;
	}
	
	/**
	 * Sets the mum counter.
	 *
	 * @param count the new mum counter
	 */
	public void setMumCounter(int count) {
		mumCounter = count;
	}
	
	/**
	 * Gets the gma penalty skipped.
	 *
	 * @return the gma penalty skipped
	 */
	public boolean getGmaPenaltySkipped() {
		return gmaPenaltySkipped;
	}
	
	/**
	 * Sets the gma penalty skipped.
	 *
	 * @param b the new gma penalty skipped
	 */
	public void setGmaPenaltySkipped(boolean b) {
		this.gmaPenaltySkipped = b;
	}
	
	/**
	 * Gets the mum added.
	 *
	 * @return the mum added
	 */
	public boolean getMumAdded() {
		return mumAdded;
	}
	
	/**
	 * Sets the mum added.
	 *
	 * @param b the new mum added
	 */
	public void setMumAdded(boolean b) {
		this.mumAdded = b;
	}

	/**
	 * Gets the statistics controller.
	 *
	 * @return the statistics controller
	 */
	public StatisticsController getStatisticsController() {
		return statisticsController;
	}

	/**
	 * Sets the statistics controller.
	 *
	 * @param statisticsController the new statistics controller
	 */
	public void setStatisticsController(StatisticsController statisticsController) {
		this.statisticsController = statisticsController;
	}

	/**
	 * Sets the role controller.
	 *
	 * @param roleController the new role controller
	 */
	public void setRoleController(RoleController roleController) {
		this.roleController = roleController;
	}

	/**
	 * Gets the max esteem.
	 *
	 * @return the max esteem
	 */
	public int getMaxEsteem() {
		return maxEsteem;
	}

	/**
	 * Sets the max esteem.
	 *
	 * @param maxEsteem the new max esteem
	 */
	public void setMaxEsteem(int maxEsteem) {
		this.maxEsteem = maxEsteem;
	}

	/**
	 * Sets the ori se.
	 *
	 * @param oriSE the new ori se
	 */
	public void setOriSE(int oriSE) {
		this.oriSE = oriSE;
	}

	/**
	 * Sets the new se.
	 *
	 * @param newSE the new new se
	 */
	public void setNewSE(int newSE) {
		this.newSE = newSE;
	}
}
